// Copyright (c) 2009, David Aiken
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the organization to which the authors belong nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY AUTHORS AND CONTRIBUTORS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL AUTHORS AND CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

/*
 *----------------------------------------------------------------------------
 * This tests the PRT read/write.
 *----------------------------------------------------------------------------
 */
#define HDK_RELAXED_GB

#include <string.h>
#include <vector>
#include <stdio.h>
#include <limits>

#include "../PRT.h"
#include "unit--.h"

#include "../Half/half.h"

using namespace HDK_PRT;

testSuite(PRTSuite);

double
readFile(PRT &prt, const char *path)
{
    prt.readFromFile(path);

    return prt.printValues(true);
}

template<typename T>
T
rand(T a, T b)
{
    return ((b-a)*(T((float)rand())/RAND_MAX))+a;
}

void
compareTotals(std::string title, PRT &prt, double total)
{
    prt.saveToFile(1, "limits.prt");
    double valTotalSaved = prt.printValues(true);
    printf("\nAttribute %s: Comparing data total %f to saved total %f", title.c_str(), total, valTotalSaved);
    if(total != valTotalSaved)
    {
        if((int)(total) != (int)(valTotalSaved))
        {
            assertTrue(false && "data total is not equal to written total");
        }
        else
        {
            printf("\nNot exact, but close enough for current needs.");
        }
    }

    // read back
    PRT readPRT;
    double valTotalRead = readFile(readPRT, "limits.prt");
    printf("\nComparing saved total %f to read total %f..", valTotalSaved, valTotalRead);
    if(valTotalSaved != valTotalRead)
    {
        assertTrue(false && "written total is not equal to read total");
   }
}

//!Fast/loose limits test
testCase(Limits, PRTSuite)
{
    remove("limits.prt");

    PRT float16;
    double total = 0;
    unsigned int id = 1;

    // write float16 min
    Array val( new unsigned char[sizeof(half)] );
    half *ph = (half*)val.get();
    *ph = -65503.0f;
    total += float(*ph);
    float16.addChannel(id++, "16min", kFLOAT16, 1, val);

    // write float16 max
    Array val2( new unsigned char[sizeof(half)] );
    ph = (half*)val2.get();
    *ph = 65503.0f;
    total += float(*ph);
    float16.addChannel(id++, "16max", kFLOAT16, 1, val2);

    compareTotals("half", float16, total);


    PRT float32;
    total = 0;

    // write float32 min
    Array val3( new unsigned char[sizeof(float)] );
    float *pf = (float*)val3.get();
    *pf = std::numeric_limits<float>::min(); // for some reason it thinks this is 0!?
    total += float(*pf);
    float32.addChannel(id++, "32min", kFLOAT32, 1, val3);

    // write float32 max
    Array val4( new unsigned char[sizeof(float)] );
    pf = (float*)val4.get();
    *pf = std::numeric_limits<float>::max();
    total += float(*pf);
    float32.addChannel(id++, "32max", kFLOAT32, 1, val4);

    compareTotals("float", float32, total);

    PRT float64;
    total = 0;

    // write float64 min
    Array val5( new unsigned char[sizeof(double)] );
    double *pd = (double*)val5.get();
    *pd = std::numeric_limits<double>::min();
    total += *pd;
    float64.addChannel(id++, "64min", kFLOAT64, 1, val5);

    // write float64 max
    Array val6( new unsigned char[sizeof(double)] );
    pd = (double*)val6.get();
    *pd = std::numeric_limits<double>::max();
    total += *pd;
    float64.addChannel(id++, "64max", kFLOAT64, 1, val6);

    compareTotals("double", float64, total);

    PRT int32;
    total = 0;

    // write int32 min
    Array val7( new unsigned char[sizeof(int)] );
    int *pi = (int*)val7.get();
    *pi = std::numeric_limits<int>::min();
    total += *pi;
    int32.addChannel(id++, "intmin", kINT32, 1, val7);

    // write int32 max
    Array val8( new unsigned char[sizeof(int)] );
    pi = (int*)val8.get();
    *pi = std::numeric_limits<int>::max();
    total += *pi;
    int32.addChannel(id++, "intmax", kINT32, 1, val8);

    compareTotals("int", int32, total);

    PRT int64;
    total = 0;

    // write int64 min
    Array val9( new unsigned char[sizeof(long long)] );
    long long *pll = (long long*)val9.get();
    *pll = std::numeric_limits<long long>::min();
    total += *pll;
    int64.addChannel(id++, "int64min", kINT64, 1, val9);

    // write int64 max
    Array val10( new unsigned char[sizeof(long long)] );
    pll = (long long*)val10.get();
    *pll = std::numeric_limits<long long>::max();
    total += *pll;
    int64.addChannel(id++, "int64max", kINT64, 1, val10);

    compareTotals("int64", int64, total);
}

//!Rough test. See also prt.hipnc
testCase(WriteRead, PRTSuite)
{
    PRT prt;
    bool testsPassed = true;

    remove("test.prt");

    double total = 0;
    unsigned int id=1;

    // write 10 float16
    half f16[10];
    unsigned int count = sizeof(f16)/sizeof(half);
    Array rawArrayFloat16Ten( new unsigned char[sizeof(half)*count] );
    half* ph = (half*)rawArrayFloat16Ten.get();
    for(unsigned int i=0;i<count;i++)
    {
        f16[i] = float(rand<half>(0,16));
        *ph = f16[i];
        total += float(f16[i]);
        ph++;
    }
    prt.addChannel(id++, "float16", kFLOAT16, 1, rawArrayFloat16Ten);

    // write 10 float32
    float f32[10];
    count = sizeof(f32)/sizeof(float);
    Array rawArrayFloat32Ten( new unsigned char[sizeof(float)*count] );
    float *pf = (float*)rawArrayFloat32Ten.get();
    for(unsigned int i=0;i<count;i++)
    {
        f32[i] = rand<float>(0,32);
        *pf = f32[i];
        total += f32[i];
        pf++;
    }
    prt.addChannel(id++, "float32", kFLOAT32, 1, rawArrayFloat32Ten);

    // write 10 float32 entries with arity 3
    float va[10][3];
    Array rawArrayVecTen( new unsigned char[sizeof(float)/sizeof(char)*3*count] );
    float* vi = (float*)rawArrayVecTen.get();
    for(unsigned int i=0;i<count;i++)
    {
        for(long long j=0;j<3;j++)
        {
            va[i][j] = rand<float>(0,10);
            *vi = va[i][j];
            total += va[i][j];
            vi++;
        }
    }
    prt.addChannel(id++, "vec32", kFLOAT32, 3, rawArrayVecTen);

    // write 10 float32 entries with arity 4
    float va4[10][4];
    Array rawArrayVec4Ten( new unsigned char[sizeof(float)/sizeof(char)*4*count] );
    float* vi4 = (float*)rawArrayVec4Ten.get();
    for(unsigned int i=0;i<count;i++)
    {
        for(unsigned int j=0;j<4;j++)
        {
            va4[i][j] = rand<float>(0,10);
            *vi4 = va4[i][j];
            total += va4[i][j];
            vi4++;
        }
    }
    prt.addChannel(id++, "vec32_4", kFLOAT32, 4, rawArrayVec4Ten);

    // write 10 double entries with arity 4
    double vad4[10][4];
    Array rawArrayVecDbl4Ten( new unsigned char[sizeof(double)/sizeof(char)*4*count] );
    double* vd4 = (double*)rawArrayVecDbl4Ten.get();
    for(unsigned int i=0;i<count;i++)
    {
        for(unsigned int j=0;j<4;j++)
        {
            vad4[i][j] = rand<double>(0,64);
            *vd4 = vad4[i][j];

            total += vad4[i][j];
            vd4++;
        }
    }
    prt.addChannel(id++, "vec32d_4", kFLOAT64, 4, rawArrayVecDbl4Ten);

    prt.saveToFile(count, "test.prt");
    double valTotalSaved = prt.printValues(true);
    printf("\nComparing data total %f to saved total %f", total, valTotalSaved);
    if(total != valTotalSaved)
    {
        if((int)(total) != (int)(valTotalSaved))
        {
            testsPassed = false;
            assertTrue(false && "data total is not equal to written total");
        }
        else
        {
            printf("\nNot exact, but close enough for current needs.");
        }
    }

    // read back
    PRT readPRT;
    double valTotalRead = readFile(readPRT, "test.prt");
    printf("\nComparing saved total %f to read total %f", valTotalSaved, valTotalRead);
    if(valTotalSaved != valTotalRead)
    {
        testsPassed = false;
        assertTrue(false && "written total is not equal to read total");
   }
   assertTrue(testsPassed && "Comparing totals of written and read data");
}
